import path from 'path';
import Express from 'express';
import favicon from 'serve-favicon';
import httpProxy from 'http-proxy';
import compression from 'compression';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { Provider } from 'react-redux';
import { match, RouterContext } from 'react-router';
import configureStore from './utils/configureStore';
import getRoutes from './routes';
import Html from './utils/Html';
import config from './config';

// Universal渲染的服务端指的是前端服务器

// 前端服务器用于渲染 React 组件并吐出多个页面，它使用了多种技术，
// 包括
// React 组件的服务端渲染、
// React Router的服务端路由、
// Webpack同构渲染、
// 数据预载和延迟渲染、
// 代理API服务器、
// 压缩等。


const app = new Express();

// 针对生产环境做了一些优化
// 1. 将端口号写在配置文件中，方便部署时端口变动。
const port = config.port;

// 2. 使用http-proxy，将API服务器代理到前端服务器，主要用于解决跨域的问题，这里也可以直接使用代理服务器。
const targetUrl = 'http://' + config.apiHost + ':' + config.apiPort;
const proxy = httpProxy.createProxyServer({
  target: targetUrl
});
// 3．采用Express的压缩拓展启动压缩，支持deflate和gzip两种压缩方式
app.use(compression());

app.use(Express.static(path.join(__dirname, '..', 'static')));
app.use(favicon(path.join(__dirname, '..', 'static', 'favicon.ico')));

app.use('/api', (req, res) => {
  proxy.web(req, res, { target: targetUrl });
});

app.use((req, res) => {
  global.__COOKIE__ = req.get('cookie');

  if (process.env.NODE_ENV !== 'production') {
    webpackIsomorphicTools.refresh();
  }

  const store = configureStore();
  const routes = getRoutes(store);

  function hydrateOnClient() {
    //4. 写React组件Html替代原来的模板字符串。
    res.send('<!doctype html>\n' +
      renderToString(<Html assets={webpackIsomorphicTools.assets()} store={store}/>));
  }

  // 5. __DISABLE_SSR__ 进制服务端 渲染
  if (__DISABLE_SSR__) {
    hydrateOnClient();
    return;
  }

  match({ routes, location: req.url }, (err, redirect, renderProps) => {
    if (redirect) {
      res.redirect(redirect.pathname + redirect.search);
    } else if (err) {
      res.status(500);
      hydrateOnClient();
      console.error('ROUTER ERROR:', err.stack);
    } else if (renderProps) {
      res.status(200);
      const component = (
        <Provider store={store}>
          <RouterContext {...renderProps} />
        </Provider>
      );
      res.send('<!doctype html>\n' +
        renderToString(
          <Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>)
      );
    } else {
      res.status(404).send('Not found');
    }
  });
});

app.listen(port, (error) => {
  if (error) {
    console.error(error);
  } else {
    console.info('==> 🌎  Open http://%s:%s in a browser to view the app.', config.host, port);
  }
});
